home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / lib / hal / scripts / hal_lpadmin next >
Encoding:
Text File  |  2009-04-06  |  37.4 KB  |  880 lines

  1. #!/usr/bin/python
  2. import dbus, sys, os, time, signal, re, glob
  3. import traceback
  4. import cups, cupshelpers
  5. import subprocess
  6. from syslog import *
  7. try:
  8.     import usb
  9. except:
  10.     pass
  11.  
  12. def find_usblp_udi(udi):
  13.     # Return always a UDI based on the usblp kernel module (has
  14.     # "_printer_" in it). If the input UDI is from low-level USB,
  15.     # find the corresponding usblp UDI.
  16.     if udi.find ("_printer_") == -1:
  17.         devnull = file ("/dev/null", "r+")
  18.         command = "hal-device | grep '[0-9]: *udi *= *.%s.*_printer_' | cut -d \\' -f 2" % udi;
  19.         try:
  20.             p = subprocess.Popen (command,
  21.                                   shell=True,
  22.                                   stdin=devnull,
  23.                                   stdout=subprocess.PIPE,
  24.                                   stderr=devnull)
  25.             (stdout, stderr) = p.communicate ()
  26.             udi = stdout.split ('\n')[0].strip ()
  27.             if udi.find ("_printer_") == -1:
  28.                 return None
  29.         except:
  30.             return None
  31.     return udi
  32.  
  33. def get_hal_property(udi, key):
  34.     # Read out the value of a given key for a given HAL UDI
  35.     devnull = file ("/dev/null", "r+")
  36.     try:
  37.         p = subprocess.Popen (["hal-get-property", "--udi", udi,
  38.                                "--key", key],
  39.                               stdin=devnull,
  40.                               stdout=subprocess.PIPE,
  41.                               stderr=devnull)
  42.         (stdout, stderr) = p.communicate ()
  43.         return stdout.strip ()
  44.     except:
  45.         return ""
  46.  
  47. def get_hal_uri(udi):
  48.     # udi is the UDI under which HAL reported the printer to us. The
  49.     # "hal" CUPS backend only works with UDIs based on the usblp
  50.     # kernel module and not with low-level UDIs. So if we are called
  51.     # with a low-level UDI here we try to find the corresponding usblp
  52.     # UDI here (contains "_printer_"). Then we return a valid CUPS URI
  53.     # for the "hal" backend ("hal://<usblp-based HAL UDI>"). If we are
  54.     # called with a low-level UDI and do not find the corresponding
  55.     # usblp-based UDI, we return None.
  56.     usblp_udi = find_usblp_udi(udi)
  57.     if usblp_udi:
  58.         return "hal://%s" % usblp_udi
  59.     else:
  60.         return None
  61.  
  62. def get_hplip_uris_for_usb (fax=False, checkuri=None):
  63.     hpuris = []
  64.     env = dict()
  65.     env.update (os.environ)
  66.     env['LC_ALL'] = 'C'
  67.     devnull = file ("/dev/null", "r+")
  68.     try:
  69.         p = subprocess.Popen (['lsusb'], env=env,
  70.                               stdin=devnull,
  71.                               stdout=subprocess.PIPE,
  72.                               stderr=devnull)
  73.         (stdout, stderr) = p.communicate ()
  74.         lsusboutput = stdout.split ('\n')
  75.     except:
  76.         if checkuri:
  77.             return False
  78.         else:
  79.             return hpuris
  80.     for line in lsusboutput:
  81.         if (line.find ("ID 03f0:") < 0): continue
  82.         bus = line[4:7]
  83.         device = line[15:18]
  84.         if fax:
  85.             type="-f"
  86.         else:
  87.             type="-c"
  88.         try:
  89.             p = subprocess.Popen (["hp-makeuri", "-lnone",
  90.                                    type, "%s:%s" % (bus, device)], env=env,
  91.                                   stdin=devnull, 
  92.                                   stdout=subprocess.PIPE,
  93.                                   stderr=devnull)
  94.             (stdout, stderr) = p.communicate ()
  95.             uri = stdout.split ('\n')[0].strip ()
  96.         except:
  97.             continue
  98.         if (not uri): continue
  99.         if checkuri and checkuri == uri:
  100.              return True
  101.         hpuris.append (uri)
  102.     if checkuri:
  103.         return False
  104.     else:
  105.         return hpuris
  106.  
  107. class HalPrinter:
  108.     def __init__(self):
  109.         self.get_properties()
  110.         self.uris = None
  111.         self.hp_fax_uris = None
  112.         try:
  113.             self.cups_connection = cups.Connection()
  114.         except RuntimeError, e:
  115.             syslog (LOG_ERR,
  116.                     "Unable to connect to CUPS: '%s'.  Is CUPS running?" % e)
  117.             sys.exit (1)
  118.  
  119.     def get_properties(self):
  120.         self.properties = {}
  121.         for key, value in os.environ.iteritems():
  122.             if key.startswith("HAL_PROP_"):
  123.                 name = key[9:].lower().replace("_", '.')
  124.                 self.properties[name] = value
  125.         self.uid = os.getenv("UDI", "")
  126.         if re.search ("_if\d+$", self.uid):
  127.             syslog (LOG_DEBUG, "hal_lpadmin triggered by low-level USB device")
  128.         else:
  129.             syslog (LOG_DEBUG, "hal_lpadmin triggered by usblp kernel module")
  130.         self.read()
  131.  
  132.     def fetch_device_id(self):
  133.         p = self.properties
  134.         devidstr = ''
  135.         buses = usb.busses()
  136.         usb_configuration_value = p.get ("usb.configuration.value")
  137.         usb_interface_number = p.get ("usb.interface.number")
  138.         for bus in buses:
  139.             if int (bus.dirname) != int (p.get ("usb.bus.number")):
  140.                 continue
  141.  
  142.             for dev in bus.devices:
  143.                 if int (dev.filename) != int (p.get("usb.linux.device.number")):
  144.                     continue
  145.  
  146.                 for config in dev.configurations:
  147.                     if (usb_configuration_value != None and
  148.                         (int (config.value) != int (usb_configuration_value))):
  149.                         continue
  150.  
  151.                     for intf in config.interfaces:
  152.                         interfaceNumber = int (intf[0].interfaceNumber)
  153.                         if (usb_interface_number != None and
  154.                             int (interfaceNumber) != 
  155.                             int (usb_interface_number)):
  156.                             continue
  157.  
  158.                         syslog (LOG_DEBUG, "Device %s:%s: %s" %
  159.                                 (bus.dirname,
  160.                                  dev.filename,
  161.                                  p.get ("info.udi")))
  162.                         handle = dev.open ()
  163.                         #handle.setConfiguration (config)
  164.                         handle.claimInterface (intf[0])
  165.                         handle.setAltInterface (intf[0])
  166.                         intfno = intf[0].interfaceNumber
  167.                         alt = intf[0].alternateSetting
  168.                         devidarr = handle.controlMsg (requestType = 0xa1, 
  169.                                                       request = 0,
  170.                                                       value = config.value - 1,
  171.                                                       index = (alt +
  172.                                                                (intfno << 8)),
  173.                                                       buffer = 4096,
  174.                                                       timeout = 100)
  175.                         siz = len(devidarr)
  176.                         len0 = devidarr[0]
  177.                         len1 = devidarr[1]
  178.                         devidlen  = (((len0 & 255) << 8) +
  179.                                      (len1 & 255))
  180.                         devidlen2 = (((len1 & 255) << 8) +
  181.                                      (len0 & 255))
  182.                         if devidlen > siz:
  183.                             if devidlen2 > siz:
  184.                                 devidlen = siz
  185.                             else:
  186.                                 devidlen = devidlen2
  187.                         devidstr = ""
  188.                         for i in devidarr[2:]:
  189.                             devidstr += chr (i)
  190.                         syslog (LOG_DEBUG, "Device ID for %s:%s: %s" %
  191.                                 (bus.dirname,
  192.                                  dev.filename,
  193.                                  devidstr))
  194.                         break
  195.  
  196.                     if len (devidstr) > 0:
  197.                         break
  198.  
  199.                     if usb_configuration_value != None:
  200.                         break
  201.                 break
  202.             break
  203.  
  204.         return devidstr
  205.  
  206.     def read(self):
  207.         p = self.properties
  208.         fetch_id = False
  209.         if p.get ("printer.vendor") == None or \
  210.            p.get ("printer.product") == None or \
  211.            p.get ("printer.description") == None or \
  212.            p.get ("printer.commandset") == None:
  213.             fetch_id = True
  214.             usblp_udi = find_usblp_udi(self.uid)
  215.             if usblp_udi:
  216.                 syslog (LOG_DEBUG,
  217.                         "Getting device ID from the usblp HAL entry ...")
  218.                 devidstr = "MFG:%s;MDL:%s;DES:%s;CMD:%s;" % \
  219.                     (get_hal_property(usblp_udi, "printer.vendor"),
  220.                      get_hal_property(usblp_udi, "printer.product"),
  221.                      get_hal_property(usblp_udi, "printer.description"),
  222.                      get_hal_property(usblp_udi, "printer.commandset").\
  223.                          replace(" ", ","))
  224.                 syslog (LOG_DEBUG, "Device ID for %s: %s" %
  225.                         (get_hal_property(usblp_udi, "printer.device"),
  226.                          devidstr))
  227.             else:
  228.                 # Printer 1284 device URI not supplied by HAL, so we poll
  229.                 # it from the printer and add it to the HAL database
  230.                 # entry. This happens usually if the printer is detected
  231.                 # via low-level USB.
  232.                 if p.get ("usb.bus.number") == None or \
  233.                    p.get ("usb.linux.device.number") == None:
  234.                     fetch_id = False
  235.                 if fetch_id:
  236.                     try: 
  237.                         syslog (LOG_DEBUG,
  238.                                 "Polling device ID from the printer ...")
  239.                         devidstr = self.fetch_device_id ()
  240.                     except Exception, e:
  241.                         # We cannot read out the device ID from the
  242.                         # device, probably because the device is not
  243.                         # really a printer. So stop hal_lpadmin here
  244.                         # ignoring the device.
  245.                         syslog (LOG_DEBUG, "Failed to fetch device ID: %s" %
  246.                                 str (e))
  247.                         syslog (LOG_DEBUG, "Ignoring device with UDI %s" %
  248.                                 self.uid)
  249.                         sys.exit(0)
  250.  
  251.         if fetch_id:
  252.             # Write it into the HAL database.
  253.             try:
  254.                 self.device_id = devidstr
  255.                 id_dict = cupshelpers.parseDeviceID (devidstr)
  256.                 self.make = (id_dict["MFG"] or p.get("usb.vendor", "Unknown"))
  257.                 p["printer.vendor"] = self.make
  258.                 self.model = (id_dict["MDL"] or p.get("usb.product", "Unknown"))
  259.                 p["printer.product"] = self.model
  260.                 self.description = id_dict["DES"]
  261.                 p["printer.description"] = self.description
  262.                 self.name = self.get_name()
  263.                 self.faxname = self.name + "_fax"
  264.                 self.commandsets = id_dict["CMD"]
  265.                 p["printer.commandset"] = '\t'.join (self.commandsets)
  266.                 self.serial = (id_dict["SN"] or p.get("usb.serial", None))
  267.                 if self.serial != None:
  268.                     p["printer.serial"] = self.serial
  269.  
  270.                 devnull = file ("/dev/null", "r+")
  271.                 env = dict()
  272.                 env.update (os.environ)
  273.                 env["LC_ALL"] = "C"
  274.                     
  275.                 for key in ("printer.vendor", "printer.product",
  276.                             "printer.description", "printer.serial"):
  277.                     if p.get (key) == None:
  278.                         continue
  279.                     try:
  280.                         pr = subprocess.Popen (["hal-set-property",
  281.                                                "--udi=%s" % self.uid,
  282.                                                "--key=%s" % key,
  283.                                                "--string=%s" % p[key]],
  284.                                               env=env,
  285.                                               stdin=devnull,
  286.                                               stdout=devnull,
  287.                                               stderr=subprocess.STDOUT)
  288.                         pr.wait ()
  289.                     except:
  290.                         pass
  291.                 for cs in self.commandsets:
  292.                     try:
  293.                         pr = subprocess.Popen (["hal-set-property",
  294.                                                "--udi=%s" % self.uid,
  295.                                                "--key=printer.commandset",
  296.                                                "--strlist-post=%s" % cs],
  297.                                               env=env,
  298.                                               stdin=devnull,
  299.                                               stdout=devnull,
  300.                                               stderr=subprocess.STDOUT)
  301.                         pr.wait ()
  302.                     except:
  303.                         pass
  304.                 syslog (LOG_DEBUG,
  305.                         "Written device ID into HAL database entry: "
  306.                         "MFG:%s;MDL:%s;DES:%s;CMD:%s;" %
  307.                         (p["printer.vendor"], p["printer.product"],
  308.                          p["printer.description"],
  309.                          p["printer.commandset"].replace("\t", ",")))
  310.             except Exception, e:
  311.                 syslog (LOG_DEBUG,
  312.                         "Failed to write device ID into HAL database entry: %s"
  313.                         % str (e))
  314.                 fetch_id = False
  315.  
  316.         if not fetch_id:
  317.             # Printer device ID is already available in the HAL database.
  318.             # Either HAL has already put it there (triggered by the usblp
  319.             # kernel module) or hal_lpadmin has read it from the printer
  320.             # when it was turned on.
  321.             syslog (LOG_DEBUG, "Using device ID from HAL database entry")
  322.             self.make = (p.get("printer.vendor", "") or
  323.                      p.get("usb.vendor", "Unknown"))
  324.             self.model = (p.get("printer.product", "") or
  325.                       p.get("usb.product", "Unknown"))
  326.             self.description = p.get("printer.description", "")
  327.             self.name = self.get_name()
  328.             self.faxname = self.name + "_fax"
  329.             self.commandsets = p.get('printer.commandset', '').split('\t')
  330.             self.serial = p.get("printer.serial", "")
  331.  
  332.             # Reconstruct Device ID ready to put it into the PPD file.
  333.             devidstr = ''
  334.             for (field, value) in [("MFG:", self.make),
  335.                                    ("MDL:", self.model),
  336.                                    ("DES:", self.description),
  337.                                    ("CMD:", reduce (lambda x, y: x + ',' + y,
  338.                                                     self.commandsets)),
  339.                                    ("SN:", self.serial)]:
  340.                 if len (value) > 0:
  341.                     devidstr += field + value + ";"
  342.  
  343.             self.device_id = devidstr
  344.  
  345.     def get_name(self):
  346.         # XXX check for unallowed chars
  347.         if self.properties.has_key("usb.port_number"):
  348.             name = "%s-%s" % (self.model,
  349.                               self.properties["usb.port_number"])
  350.         else:
  351.             name = self.model
  352.         name = name.replace(" ", "-")
  353.         name = name.replace("/", "-")
  354.         return name.replace("#", "-")
  355.  
  356.     def get_cups_uris(self, removed=False):
  357.         if self.uris != None:
  358.             return self.uris
  359.         uris = []
  360.         available_usb_uris = []
  361.         for backend in ("hal", "usb", "hp"):
  362.             devnull = file ("/dev/null", "r+")
  363.             command = "/usr/lib/cups/backend/%s | cut -s -d ' ' -f 2" % backend;
  364.             try:
  365.                 p = subprocess.Popen (command,
  366.                                       shell=True,
  367.                                       stdin=devnull,
  368.                                       stdout=subprocess.PIPE,
  369.                                       stderr=devnull)
  370.                 (stdout, stderr) = p.communicate ()
  371.                 available_usb_uris = stdout.split ('\n') + \
  372.                     available_usb_uris
  373.             except:
  374.                 pass
  375.         udi = self.uid.lower ()
  376.         make = ''
  377.         model = ''
  378.         serial = ''
  379.         if self.properties.has_key("printer.vendor"):
  380.             make = self.properties["printer.vendor"].lower ()
  381.             if make == "hewlett-packard":
  382.                 make = "hp"
  383.             elif make == "lexmark international":
  384.                 make = "lexmark"
  385.         if self.properties.has_key("printer.product"):
  386.              model = self.properties["printer.product"].lower ()
  387.              if make and model.startswith (make):
  388.                  model = model[len (make):]
  389.                  model = model.lstrip ()
  390.              model = model.rstrip ()
  391.         if self.properties.has_key("printer.serial"):
  392.              serial = self.properties["printer.serial"].lower ()
  393.         for uri in available_usb_uris:
  394.             if uri.lower ().find (udi) != -1:
  395.                 uris.append (uri)
  396.             elif uri.lower ().find (serial) != -1:
  397.                 uris.append (uri)
  398.             else:
  399.                 # If we arrive here, the printer's serial number did not
  400.                 # match the currently investigated URI. Skip this URI
  401.                 # if it contains another serial number
  402.                 if uri.lower().find("?serial=") != -1: continue
  403.                 nuri = re.sub(r"(\%\d\d)+", " ", uri.lower())
  404.                 nuri = re.sub(r"[^a-z0-9]+", " ", nuri)
  405.                 nmake = re.sub(r"[^a-z0-9]+", " ", make)
  406.                 nmodel = re.sub(r"[^a-z0-9]+", " ", model)
  407.                 if nuri.find (nmake) != -1 and nuri.find (nmodel) != -1:
  408.                     uris.append (uri)
  409.         self.uris = uris
  410.         return uris
  411.  
  412.     def get_cups_uri(self):
  413.         try:
  414.             return self.get_cups_uris()[0]
  415.         except:
  416.             return None
  417.  
  418.     def get_cups_hp_fax_uris(self, removed=False):
  419.         if self.hp_fax_uris != None:
  420.             return self.hp_fax_uris
  421.         faxurisfound = 0
  422.         if self.properties.has_key("printer.vendor"):
  423.             vendor = self.properties["printer.vendor"].lower ()
  424.             if (not removed and
  425.                 (vendor == "hewlett-packard" or vendor == "hp")):
  426.                 # We only can have a fax URI if we have an HP printer
  427.                 # supported by HPLIP
  428.                 try:
  429.                     # Try to find a matching HPLIP fax URI for the HPLIP
  430.                     # print URI for this device
  431.                     hpfaxuris = get_hplip_uris_for_usb (True)
  432.                     uris = self.get_cups_uris ()
  433.                     faxuris = []
  434.                     for uri in uris:
  435.                         if not uri.startswith ("hp:"): continue
  436.                         faxuri = uri.replace("hp:/", "hpfax:/")
  437.                         for furi in hpfaxuris:
  438.                             if faxuri == furi:
  439.                                 faxurisfound = 1
  440.                                 faxuris.append (faxuri)
  441.                 except:
  442.                     pass
  443.         
  444.         if faxurisfound == 1:
  445.             self.hp_fax_uris = faxuris
  446.             return faxuris
  447.         else:
  448.             return None
  449.  
  450.     def get_cups_hp_fax_uri(self):
  451.         faxuris = self.get_cups_hp_fax_uris()
  452.         if faxuris:
  453.             return faxuris[0]
  454.         else:
  455.             return None
  456.  
  457.     def store_device_id_in_ppd(self):
  458.         fname = self.cups_connection.getPPD(self.name)
  459.         lines = file (fname).readlines ()
  460.         attr = "*1284DeviceID:"
  461.         has_1284_attr = reduce (lambda x, y: x or y,
  462.                                 map (lambda x: x.startswith (attr)))
  463.         outf = file (fname, 'w')
  464.         written = False
  465.         attrline = attr + ' ' + self.device_id + '\n'
  466.         for line in lines:
  467.             if not written:
  468.                 if has_1284_attr:
  469.                     if line.startswith (attr):
  470.                         # Replace existing attribute.
  471.                         line = attrline
  472.                         written = True
  473.                 else:
  474.                     if line == '\n':
  475.                         # Write attribute before first blank line.
  476.                         line = attrline + line
  477.                         written = True
  478.  
  479.             outf.write (line)
  480.         if not written:
  481.             outf.write (attrline)
  482.         outf.close ()
  483.         self.cups_connection.addPrinter(self.name, filename=fname)
  484.         os.unlink (fname)
  485.  
  486.     def printer_is_our_hal_printer(self, name, printer):
  487.         make = self.properties.get ("printer.vendor", None)
  488.         model = self.properties.get ("printer.product", None)
  489.         serial = self.properties.get ("printer.serial", None)
  490.         if not serial:
  491.             serial = self.properties.get ("info.udi", None)
  492.             if not serial:
  493.                 serial = self.properties.get ("info.parent", None)
  494.             if serial:
  495.                 res = re.search ("usb_device_[0-9a-fA-F]+_[0-9a-fA-F]+_([0-9a-zA-Z]+)", serial)
  496.                 if res:
  497.                     resg = res.groups()
  498.                     serial = resg[0]
  499.         bus = self.properties.get ("linux.subsystem", None)
  500.         udi = self.properties.get ("info.udi", None)
  501.         if make:
  502.             makel = make.lower ()
  503.             if makel == "hewlett-packard":
  504.                 make = "HP"
  505.             elif makel == "lexmark international":
  506.                 make = "Lexmark"
  507.         if model:
  508.             if model.startswith (make):
  509.                 model = model[len (make):]
  510.                 model = model.lstrip ()
  511.         if printer.is_class:
  512.             return False
  513.         if (((model and 
  514.               (printer.device_uri.find (model.replace (" ", "%20")) \
  515.                    != -1 or
  516.                printer.device_uri.find (model.replace (" ", "_")) \
  517.                    != -1)) and
  518.              (not serial or printer.device_uri.find ("serial=") == -1 or
  519.               (serial and
  520.                printer.device_uri.find ("serial=" + serial) != -1)) and
  521.              (not bus or
  522.               printer.device_uri.find (bus) != -1)) or
  523.             (udi and printer.device_uri.find (udi) != -1) or
  524.             (serial and
  525.              printer.device_uri.find ("serial=" + serial) != -1)):
  526.             syslog (LOG_DEBUG,
  527.                     "Found configured printer: %s; URI: %s" % 
  528.                     (name, printer.device_uri))
  529.             return True
  530.         return False
  531.  
  532.     def add(self):
  533.         syslog (LOG_DEBUG, "add")
  534.         make = self.properties.get ("printer.vendor", None)
  535.         model = self.properties.get ("printer.product", None)
  536.         serial = self.properties.get ("printer.serial", None)
  537.         syslog (LOG_DEBUG, "Printer reported by HAL: %s %s %s" % (make, model, serial))
  538.         printers = cupshelpers.getPrinters(self.cups_connection)
  539.         printers_extra_info = None
  540.         printer_exists = 0
  541.         fax_exists = 0
  542.         for name, printer in printers.iteritems():
  543.             if self.printer_is_our_hal_printer(name, printer):
  544.                 if printer.device_uri.startswith("hpfax:"):
  545.                     fax_exists = 1
  546.                 else:
  547.                     printer_exists = 1
  548.                 syslog (LOG_DEBUG,
  549.                         "Not adding printer: %s already exists" % name)
  550.                 printer_exists = 1
  551.                 if not printer.enabled:
  552.                     if printers_extra_info == None:
  553.                         printers_extra_info = self.cups_connection.getPrinters()
  554.                     statemsg = printers_extra_info[name]["printer-state-message"]
  555.                     if statemsg.lower ().startswith ("unplugged"):
  556.                         syslog (LOG_INFO,
  557.                                 "Re-enabling printer %s" % name)
  558.                         self.cups_connection.enablePrinter(name)
  559.                     else:
  560.                         syslog (LOG_INFO,
  561.                                 "Printer %s exists but is disabled, reason: %s; "
  562.                                 "use 'cupsenable %s' to enable it" % (name, statemsg, name))
  563.  
  564.         # Get some info needed to create CUPS queues and stop here if no new
  565.         # queues are needed
  566.         uris = self.get_cups_uris ()
  567.         faxuris = self.get_cups_hp_fax_uris ()
  568.         if printer_exists and (fax_exists or not faxuris):
  569.             syslog (LOG_INFO, "No print queue setup needed for printer with UDI %s, as queues are already in place." % self.uid)
  570.             return
  571.         if not uris:
  572.             syslog (LOG_INFO, "Could not treat printer with UDI %s, no CUPS URI found for it." % self.uid)
  573.             return
  574.         ppds = None
  575.         syslog (LOG_DEBUG, "URIs: %s" % uris)
  576.         syslog (LOG_DEBUG, "HPLIP Fax URIs: %s" % faxuris)
  577.  
  578.         # Make the name unique.
  579.         if self.name in printers.keys ():
  580.             suffix = 2
  581.             while (self.name + str (suffix)) in printers.keys ():
  582.                 suffix += 1
  583.                 if suffix == 100:
  584.                     break
  585.             self.name += str (suffix)
  586.  
  587.         # Make the faxname unique
  588.         if self.faxname in printers.keys ():
  589.             suffix = 2
  590.             while (self.faxname + str (suffix)) in printers.keys ():
  591.                 suffix += 1
  592.                 if suffix == 100:
  593.                     break
  594.             self.faxname += str (suffix)
  595.  
  596.         def wait_child (sig, stack):
  597.             (pid, status) = os.wait ()
  598.  
  599.         signal.signal (signal.SIGCHLD, wait_child)
  600.         pid = os.fork ()
  601.         if pid == 0:
  602.             # Child.
  603.             if fax_exists == 0:
  604.                 # really new fax printer
  605.                 faxuri = self.get_cups_hp_fax_uri()
  606.             else:
  607.                 faxuri = None
  608.  
  609.             if printer_exists == 0 or faxuri:
  610.                 # really new printer or fax - show tray icon with magnifier
  611.                 bus = dbus.SystemBus()
  612.                 try:
  613.                     syslog (LOG_DEBUG, "Calling GetReady")
  614.                     obj = bus.get_object("com.redhat.NewPrinterNotification",
  615.                                          "/com/redhat/NewPrinterNotification")
  616.                     notification = dbus.Interface(obj,
  617.                                                   "com.redhat.NewPrinterNotification")
  618.                     notification.GetReady ()
  619.                 except dbus.DBusException, e:
  620.                     syslog (LOG_DEBUG, "D-Bus method call failed: %s" % e)
  621.                     notification = None
  622.             else:
  623.                 notification = None
  624.  
  625.             status = 999
  626.             if printer_exists == 0:
  627.                 # really new printer
  628.  
  629.                 # If it is an HP printer, check whether it requires a
  630.                 # plugin and do not create a queue if it does
  631.                 plugin_needed = -1
  632.                 uri = self.get_cups_uri()
  633.                 if uri and uri.startswith ("hp:"):
  634.                     env = dict()
  635.                     env.update (os.environ)
  636.                     env['LC_ALL'] = 'C'
  637.                     devnull = file ("/dev/null", "r+")
  638.                     try:
  639.                         p = subprocess.Popen (["hp-info", "-i",
  640.                                                "-d%s" % uri],
  641.                                               env=env,
  642.                                               stdin=devnull,
  643.                                               stdout=subprocess.PIPE,
  644.                                               stderr=devnull)
  645.                         properties = p.stdout.read ().split ("\n")
  646.                     except:
  647.                         # Problem executing command.
  648.                         plugin_needed = 0
  649.                     if plugin_needed < 0:
  650.                         hplip_version = None
  651.                         for line in properties:
  652.                             if line.find ("plugin ") >= 0:
  653.                                 res = re.search ("(\d+)", line)
  654.                                 if res:
  655.                                     resg = res.groups()
  656.                                     plugin_needed = int(resg[0])
  657.                             elif line.find ("HP Linux Imaging and Printing") >= 0:
  658.                                 res = re.search ("(\d+\.\d+\.\d+\w*)", line)
  659.                                 if res:
  660.                                     resg = res.groups()
  661.                                     hplip_version = resg[0]
  662.                             if plugin_needed >= 0:
  663.                                 break
  664.                         if plugin_needed > 0 and hplip_version:
  665.                             # Check whether the plugin is already installed
  666.                             if glob.glob("/usr/share/hplip/data/plugin/*%s*plugin*" %
  667.                                          hplip_version):
  668.                                 if hplip_version.startswith("2"):
  669.                                     try:
  670.                                         f = open('/etc/hp/hplip.conf', 'r')
  671.                                         for line in f:
  672.                                             if line.strip ().startswith("plugin") and \
  673.                                                     line.strip ().endswith("1"):
  674.                                                 f.close()
  675.                                                 plugin_needed = 0
  676.                                                 f.close()
  677.                                     except:
  678.                                         pass
  679.                                 else:
  680.                                     plugin_needed = 0
  681.                 if uri and plugin_needed <= 0:
  682.                     # No HPLIP supported printer or no plugin needed, try to
  683.                     # find a driver and set up the print queue
  684.                     if ppds == None:
  685.                         cupsppds = self.cups_connection.getPPDs ()
  686.                         ppds = cupshelpers.ppds.PPDs (cupsppds)
  687.                     syslog (LOG_DEBUG, "Device ID: MFG:%s;MDL:%s;DES:%s;CMD:%s; URI:%s" %
  688.                             (self.make, self.model, self.description,
  689.                              reduce(lambda x, y: x + ',' + y, self.commandsets),
  690.                              uri))
  691.                     (status, ppdname) = \
  692.                         ppds.getPPDNameFromDeviceID (self.make, self.model,
  693.                                                      self.description,
  694.                                                      self.commandsets,
  695.                                                      uri)
  696.                     syslog (LOG_DEBUG, "PPD: %s; Status: %d" % (ppdname, status))
  697.  
  698.                     info = "%s %s" % (self.make, self.model)
  699.                     self.device = uri
  700.  
  701.                     if status == 0:
  702.                         self.cups_connection.addPrinter(self.name,
  703.                                                         device=self.device,
  704.                                                         ppdname=ppdname,
  705.                                                         info=info,
  706.                                                         location=os.uname ()[1])
  707.                         try:
  708.                             self.store_device_id_in_ppd ()
  709.                         except:
  710.                             pass
  711.  
  712.                         cupshelpers.activateNewPrinter (self.cups_connection, self.name)
  713.                         syslog (LOG_INFO, "Added printer %s" % self.name)
  714.                     else:
  715.                         self.name = self.device
  716.                         syslog (LOG_INFO, "Did not add printer with URI %s, no exact model fit found" % self.name)
  717.                 else:
  718.                     self.name = uri
  719.                     syslog (LOG_INFO, "Did not add printer with URI %s, requires HPLIP plugin" % self.name)
  720.  
  721.             if faxuri:
  722.                 faxname = self.faxname
  723.                 if p == None:
  724.                     cupsppds = self.cups_connection.getPPDs ()
  725.                     p = cupshelpers.ppds.PPDs (cupsppds)
  726.                 (status, faxppd) = p.getPPDNameFromDeviceID ("HP", "Fax",
  727.                                                         "HP Fax", [], faxuri)
  728.                 info = "Fax queue for %s %s" % (self.make, self.model)
  729.                 self.cups_connection.addPrinter(faxname, device=faxuri,
  730.                                                 ppdname=faxppd, info=info,
  731.                                                 location=os.uname()[1])
  732.                 self.cups_connection.enablePrinter(faxname)
  733.                 self.cups_connection.acceptJobs(faxname)
  734.                 syslog (LOG_INFO, "Added fax printer %s" % faxname)
  735.  
  736.             if notification:
  737.                 if faxuri and printer_exists != 0:
  738.                     # Only fax queue
  739.                     n = faxname
  740.                     m = self.model + " (Fax)"
  741.                 else:
  742.                     n = self.name
  743.                     m = self.model
  744.                 try:
  745.                     notification.NewPrinter (status, n,
  746.                                              self.make, m,
  747.                                              self.description,
  748.                                              reduce(lambda x, y: x + ',' + y,
  749.                                                     self.commandsets))
  750.                 except dbus.DBusException:
  751.                     pass
  752.  
  753.         elif pid == -1:
  754.             pass # should handle error
  755.  
  756.     def remove(self):
  757.         syslog (LOG_DEBUG, "remove")
  758.         # Disable all print queues which print to the device which
  759.         # we detected as having been removed. This prevents from
  760.         # jobs being retried every 30 seconds. The jobs wait in the
  761.         # queue until the device is reconnected and turned on.
  762.         #
  763.         # We cannot ask CUPS for the HPLIP URIs after having unplugged or
  764.         # turned off the printer. So we take model name and serial number
  765.         # provided by HAL and search the print queues whose URIs contain
  766.         # this model name and serial number. These are then the queues
  767.         # which we will disable.
  768.         make = self.properties.get ("printer.vendor", None)
  769.         model = self.properties.get ("printer.product", None)
  770.         serial = self.properties.get ("printer.serial", None)
  771.         syslog (LOG_DEBUG, "Printer reported by HAL: %s %s %s" % (make, model, serial))
  772.         printers = cupshelpers.getPrinters(self.cups_connection)
  773.         for name, printer in printers.iteritems():
  774.             if self.printer_is_our_hal_printer(name, printer):
  775.                 if printer.enabled:
  776.                     # Check whether the parent UDI also disappeared
  777.                     parentdied = False
  778.                     parentudi = self.properties.get ("info.parent", None)
  779.                     if (parentudi and re.search ("_if\d+$", parentudi)):
  780.                         devnull = file ("/dev/null", "r+")
  781.                         try:
  782.                             pr = subprocess.Popen (["hal-device", parentudi],
  783.                                                    stdin=devnull,
  784.                                                    stdout=devnull,
  785.                                                    stderr=subprocess.STDOUT)
  786.                             pr.wait ()
  787.                             parentdied = (pr.returncode == 1);
  788.                         except:
  789.                             pass
  790.                     # Consider the device disconnected if either we were
  791.                     # triggered by the low-level UDI disappearing or in
  792.                     # case that we were triggered by the high-level (usblp
  793.                     # kernel module) UDI disappearing that then our parent
  794.                     # UDI (which is the low-level UDI) also disappeared.
  795.                     udi = self.properties.get ("info.udi", None)
  796.                     if (udi and re.search ("_if\d+$", udi)) or parentdied:
  797.                         self.cups_connection.disablePrinter(name,
  798.                                                             "Unplugged or turned off")
  799.                         syslog (LOG_INFO,
  800.                                 "Disabled printer %s, as the corresponding device was unplugged or turned off" % (name))
  801.  
  802.     def configure(self):
  803.         syslog (LOG_DEBUG, "configure")
  804.         make, model = sys.stdin.readlines()
  805.         if make[-1]=="\n": make = make[:-1]
  806.         if model[-1]=="\n": model = model[:-1]
  807.  
  808.         cupsppds = self.cups_connection.getPPDs ()
  809.         p = cupshelpers.ppds.PPDs (cupsppds)
  810.         (status, ppdname) = p.getPPDNameFromDeviceID (make, model, "", "")
  811.  
  812.         if not ppdname:
  813.             syslog (LOG_ERR,
  814.                     "User-selected make/model \"%s\" \"%s\" not found" %
  815.                     (make, model))
  816.             return
  817.  
  818.         # add printer
  819.         self.cups_connection.addPrinter(
  820.             self.name, device=self.get_cups_uri(),
  821.             ppdname=ppdname, info="Added by HAL",
  822.             location=os.uname()[1])
  823.         self.cups_connection.enablePrinter(self.name)
  824.         self.cups_connection.acceptJobs(self.name)
  825.         syslog (LOG_INFO,
  826.                 "Added printer %s with user-selected make/model" % self.name)
  827.  
  828. class HalLpAdmin:
  829.  
  830.     def __init__(self):
  831.         if len(sys.argv)!=2:
  832.             return self.usage()
  833.  
  834.         if sys.argv[1]=="--add":
  835.             self.addPrinter()
  836.         elif sys.argv[1]=="--remove":
  837.             self.removePrinter()
  838.         elif sys.argv[1]=="--configure":
  839.             self.configurePrinter()
  840.         else:
  841.             return self.usage()
  842.  
  843.     def usage(self):
  844.         print "Usage: hal_lpadmin (--add|--remove|--configure)"
  845.  
  846.     def addPrinter(self):
  847.         printer = HalPrinter()
  848.         if printer.make:
  849.             printer.add()
  850.         
  851.     def removePrinter(self):
  852.         printer = HalPrinter()
  853.         if printer.make:
  854.             printer.remove()
  855.  
  856.     def configurePrinter(self):
  857.         printer = HalPrinter()
  858.         if printer.make:
  859.             printer.configure()
  860.  
  861. def main():
  862.     openlog ("hal_lpadmin", 0, LOG_DAEMON)
  863.     syslog (LOG_DEBUG, "Running hal_lpadmin")
  864.     time.sleep (1) # Give HPLIP a chance to reconnect
  865.     try:
  866.         h = HalLpAdmin()
  867.     except SystemExit, e:
  868.         sys.exit (e)
  869.     except:
  870.         (type, value, tb) = sys.exc_info ()
  871.         tblast = traceback.extract_tb (tb, limit=None)
  872.         if len (tblast):
  873.             tblast = tblast[:len (tblast) - 1]
  874.         for line in traceback.format_tb (tb):
  875.             syslog (LOG_ERR, line.strip ())
  876.         extxt = traceback.format_exception_only (type, value)
  877.         syslog (LOG_ERR, extxt[0].strip ())
  878.  
  879. main()
  880.